import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { DiscordSnowflake } from "@sapphire/snowflake";
import { plainToInstance } from "class-transformer";
import { DataSource, Repository } from "typeorm";
import { ResultMsg } from "@/common/enum/result-msg.enum";
import Result from "@/common/utils/result.util";
import { SysRole } from "@/database/entity/SysRole";
import { SysRoleMenu } from "@/database/entity/SysRoleMenu";
import { SysRolePermission } from "@/database/entity/SysRolePermission";
import { SysUserRole } from "@/database/entity/SysUserRole";
import { CreateRoleDto } from "./dto/create-role.dto";
import { PageRoleDto } from "./dto/page-role.dto";
import { UpdateRoleDto } from "./dto/update-role.dto";
import { UpdateRoleAuthDto } from "./dto/update-role-auth.dto";
import { RoleDetailVo } from "./vo/role-detail.vo";
import { RoleListVo } from "./vo/role-list.vo";

@Injectable()
export class RoleService {
    constructor(
        @InjectRepository(SysRole) private readonly roleRepository: Repository<SysRole>,
        @InjectRepository(SysUserRole) private readonly userRoleRepository: Repository<SysUserRole>,
        private dataSource: DataSource,
    ) {}

    /** 新增角色 */
    async create(role: CreateRoleDto) {
        const id = DiscordSnowflake.generate().toString();
        const newRole = this.roleRepository.create({ ...role, id });
        await this.roleRepository.save(newRole);
        return Result.success(id, ResultMsg.CREATE_SUCCESS);
    }

    /** 删除角色 */
    async remove(id: string) {
        const existUser = await this.userRoleRepository.exists({ where: { roleId: id } });
        if (existUser) {
            return Result.error(ResultMsg.DELETE_HAS_USER);
        }
        await this.dataSource.transaction(async (manager) => {
            const rolePermissionRepository = manager.getRepository(SysRolePermission);
            const roleMenuRepository = manager.getRepository(SysRoleMenu);
            // 先删除角色关联的菜单和权限
            await rolePermissionRepository.delete({ roleId: id });
            await roleMenuRepository.delete({ roleId: id });
            // 再删除角色本身
            await manager.update(SysRole, id, { deletedAt: new Date() });
        });
        return Result.success(id, ResultMsg.DELETE_SUCCESS);
    }

    /** 更新角色 */
    async update(role: UpdateRoleDto) {
        await this.roleRepository.update(role.id, { ...role });
        return Result.success(null, ResultMsg.UPDATE_SUCCESS);
    }

    /** 更新角色的菜单与权限 */
    async updateRolePermissions(data: UpdateRoleAuthDto) {
        const { roleId, menuIds, permIds } = data;
        await this.dataSource.transaction(async (manager) => {
            const rolePermissionRepository = manager.getRepository(SysRolePermission);
            const roleMenuRepository = manager.getRepository(SysRoleMenu);
            // 先删除原有权限
            await rolePermissionRepository.delete({ roleId });
            await roleMenuRepository.delete({ roleId });
            // 再新增新的权限
            const rolePermissions: SysRolePermission[] = [];
            const roleMenus: SysRoleMenu[] = [];
            for (const perm of permIds) {
                rolePermissions.push(rolePermissionRepository.create({ roleId, permId: perm }));
            }
            for (const menu of menuIds) {
                roleMenus.push(roleMenuRepository.create({ roleId, menuId: menu }));
            }
            if (rolePermissions.length > 0) {
                await rolePermissionRepository.save(rolePermissions);
            }
            if (roleMenus.length > 0) {
                await roleMenuRepository.save(roleMenus);
            }
        });
        return Result.success(null);
    }

    /** 查看详情 */
    async findById(id: string): Promise<Result<RoleDetailVo>> {
        const role = await this.roleRepository.findOne({
            select: ["id", "name", "code", "remark"],
            where: { id },
        });
        const detailVo = plainToInstance(RoleDetailVo, role, { excludeExtraneousValues: true });
        return Result.success(detailVo);
    }

    /** 获取角色分页列表 */
    async page(data: PageRoleDto): Promise<Result<RoleListVo[]>> {
        const { name, code, pageNum, pageSize } = data;
        const queryBuilder = this.roleRepository.createQueryBuilder("role");
        queryBuilder.select(["role.id", "role.name", "role.code", "role.createdAt", "role.updatedAt"]);
        if (name) {
            queryBuilder.andWhere("role.name LIKE :name", { name: `%${name}%` });
        }
        if (code) {
            queryBuilder.andWhere("role.code LIKE :code", { code: `%${code}%` });
        }
        queryBuilder
            .orderBy("role.createdAt", "DESC")
            .orderBy("role.name", "ASC")
            .skip((pageNum - 1) * pageSize)
            .take(pageSize);
        const [list, total] = await queryBuilder.getManyAndCount();
        const voList = plainToInstance(RoleListVo, list, { excludeExtraneousValues: true });
        return Result.page(voList, { pageNum, pageSize, total });
    }

    /** 获取角色的菜单和权限的ID集合 */
    async findRolePermissions(roleId: string) {
        const roleMenus = await this.dataSource.getRepository(SysRoleMenu).find({ where: { roleId } });
        const rolePermissions = await this.dataSource.getRepository(SysRolePermission).find({ where: { roleId } });
        const menuIds = roleMenus.map((rm) => rm.menuId);
        const permIds = rolePermissions.map((rp) => rp.permId);
        return Result.success({ menuIds, permIds });
    }
}
